from PyQt5.QtWidgets import (QApplication, QWidget, QTableWidget, QTreeWidget,
                             QGraphicsView, QGraphicsScene, QTabWidget,
                             QTextEdit, QDockWidget)
from PyQt5.QtWidgets import QHeaderView, QAbstractItemView, QTableWidgetItem, QTreeWidgetItem
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QGraphicsPixmapItem, QFrame, QApplication
from PyQt5.QtGui import QIcon, QImage, QPixmap
from PyQt5.QtCore import Qt
from app_settoolbar import ToolBarWindow
from MyFunc import Debuger, ImageBox
import sys
import os
import time
import json
import re
import cv2


class OpratorWindow(ToolBarWindow):
    def __init__(self):
        super(OpratorWindow, self).__init__()

        self.setCentralWidget(QWidget())
        self.opratorWidget = self.centralWidget()
        # ----- 设置中心窗口并获取 ----- #

        self.imageDockWidget = QDockWidget('预载入视图', self)

        self.imageTableWidget = QTableWidget(self)
        self.imageDockWidget.setWidget(self.imageTableWidget)
        self.imageTableWidget.setMinimumWidth(int(self.mainWindowWidth / 10 * 1))
        self.imageTableWidget.setColumnCount(3)
        self.imageTableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.imageTableWidget.setHorizontalHeaderLabels(('Name', 'Size', 'ChangeTime'))
        self.imageTableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.imageTableWidget.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.imageTableWidget.setTextElideMode(Qt.ElideLeft)
        self.imageTableWidget.setSelectionMode(QAbstractItemView.SingleSelection)
        self.imageTableWidget.itemSelectionChanged.connect(self.__imageTableWidget)
        self.imageTableWidget.setShowGrid(False)

        self.addDockWidget(Qt.LeftDockWidgetArea, self.imageDockWidget)
        # ----- 预载入图片信息列表活动窗口 ----- #

        self.splitDockWidget = QDockWidget('标注分类视图', self)

        self.splitTreeWidget = QTreeWidget(self)
        self.splitTreeWidget.setMinimumWidth(int(self.mainWindowWidth / 10 * 1))
        self.splitTreeWidget.setColumnCount(2)
        self.splitTreeWidget.setHeaderLabels(['对象', '坐标'])

        self.splitDockWidget.setWidget(self.splitTreeWidget)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.splitDockWidget)
        # ----- 目标检测类别树状图活动窗口 ----- #

        self.showGraphicsView = QGraphicsView(self)
        self.showGraphicsView.setMinimumSize(int(self.mainWindowWidth / 10 * 8), int(self.mainWindowHeight / 7 * 6))
        self.showGraphicsView.setFrameShadow(QFrame.Plain)
        self.showGraphicsView.setFrameShape(QFrame.StyledPanel)

        self.showGraphicsScene = QGraphicsScene(self)
        self.showGraphicsScene.setSceneRect(0, 0, self.showGraphicsView.width(), self.showGraphicsView.height())
        self.showGraphicsView.setScene(self.showGraphicsScene)
        self.setCentralWidget(self.showGraphicsView)
        # ----- 中央图片操作窗口 ----- #

        self.debugDockWidget = QDockWidget('输出调试信息', self)

        self.debugTabWidget = QTabWidget(self)
        self.debugTabWidget.setMinimumHeight(int(self.mainWindowHeight / 7 * 1))

        self.debugDockWidget.setWidget(self.debugTabWidget)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.debugDockWidget)
        # ----- 调试输出活动窗口 ----- #

        self.outDockWidget = QDockWidget('结果生成视图', self)

        self.outTabWidget = QTabWidget(self)
        self.outTabWidget.setMinimumWidth(int(self.mainWindowWidth / 10 * 1))

        self.outDockWidget.setWidget(self.outTabWidget)
        self.addDockWidget(Qt.RightDockWidgetArea, self.outDockWidget)
        # ----- 输出文件列表选项卡活动窗口 ----- #

        self.outInfoDockWidget = QDockWidget('结果详情视图', self)

        self.outInfoTextEdit = QTextEdit(self)
        self.outInfoTextEdit.setMinimumWidth(int(self.mainWindowWidth / 10 * 1))

        self.outInfoDockWidget.setWidget(self.outInfoTextEdit)
        self.addDockWidget(Qt.RightDockWidgetArea, self.outInfoDockWidget)
        # ----- 输出信息富文本活动窗口 ----- #

        self.actionFileCreateProject.triggered.connect(self.__actionCreateProject)
        self.actionFileOpenProject.triggered.connect(self.__actionOpenProject)
        self.actionSaveProject.triggered.connect(self.__actionSaveProject)
        self.actionSaveNowProject.triggered.connect(self.__actionSaveNowProject)
        self.actionExit.triggered.connect(self.__actionExit)

        self.actionNextPage.triggered.connect(self.__actionNextPage)
        self.actionLastPage.triggered.connect(self.__actionLastPage)
        self.actionDeletePage.triggered.connect(self.__actionDeletePage)
        self.actionAddPage.triggered.connect(self.__actionAddPage)

        self.actionNewLabel.triggered.connect(self.__actionNewLabel)
        self.actionEditLabel.triggered.connect(self.__actionEditLabel)
        self.actionDeleteLabel.triggered.connect(self.__actionDeleteLabel)

        self.actionUndoPixel.triggered.connect(self.__actionUndoPixel)
        self.actionRedoPixel.triggered.connect(self.__actionRedoPixel)
        self.actionRefreshPixel.triggered.connect(self.__actionRefreshPixel)

        self.actionMutiRename.triggered.connect(self.__actionMutiRename)
        self.actionChangeFileSpace.triggered.connect(self.__actionChangeFileSpace)
        self.actionOutDelete.triggered.connect(self.__actionOutDelete)
        self.actionOutDeleteAll.triggered.connect(self.__actionOutDeleteAll)
        self.actionOut.triggered.connect(self.__actionOut)

        # ----- Action方法函数绑定 ----- #

        """
            接下来是重头戏了!
        """

    def __imageTableWidget(self):
        """
        描述：预载入图像列表选择
        功能：选中时，在图像操作区展示该图片，并且可以对图片进行删除，右键可以向上和向下插入新图片
        参数：无
        返回值：无
        """
        self.actionDeletePage.setEnabled(True)
        self.actionNextPage.setEnabled(True)
        self.actionLastPage.setEnabled(True)
        self.debuger_ImgTabWid = Debuger(100, True, 'ImageTableWidget')
        try:
            filenameItem, fileSizeMBItem, fileLastTimeItem = self.imageTableWidget.selectedItems()
        except:
            return

        fileName = filenameItem.data(Qt.DisplayRole)
        filePath = None
        try:
            filePath = self.projectFileContent[0]['PreLoadImageContent'][self.imageTableWidget.currentRow()]['fileAbsPath']
        except IndexError:
            self.debuger_ImgTabWid.pushInfo(f'{(self.projectFileContent[0]["PreLoadImageContent"], self.imageTableWidget.currentRow())}')
        fileSize = fileSizeMBItem.data(Qt.DisplayRole)

        imageCV2 = cv2.imread(filePath)
        if imageCV2 is not None:
            imageCV2 = cv2.cvtColor(imageCV2, cv2.COLOR_BGR2RGB)
            imageRows, imageCols, imageChanel = imageCV2.shape
            bytesPerLine = imageCols * imageChanel

            imageQt = QImage(imageCV2, imageCols, imageRows, bytesPerLine, QImage.Format_RGB888)

            imageQPixmap = QPixmap.fromImage(imageQt)
            imageGraph = QGraphicsPixmapItem(imageQPixmap)

            try:
                imageDic = self.globalImageDics[fileName]
            except Exception as e:
                repr(e)
                imageDic = {}

            imageBox = ImageBox(fileName, filePath, fileSize, imageCV2,
                                imageQt, imageGraph, imageDic)
            self.globalImageBox = imageBox
            # ----- 获取列表中图片信息，送入全局图像盒子实例中 ----- #

            self.showGraphicsScene.clear()

            imgSourceWidth = self.globalImageBox.ImageQt.width()
            imgSourceHeight = self.globalImageBox.ImageQt.height()
            areaSourceWidth = self.showGraphicsScene.width()
            areaSourceHeight = self.showGraphicsScene.height()
            self.debuger_ImgTabWid.pushInfo(f'{(areaSourceWidth, areaSourceHeight)}')
            scaleNumHeight = 1
            scaleNumWidth = 1
            if imgSourceHeight > areaSourceHeight:
                scaleNumHeight = round((areaSourceHeight / imgSourceHeight), 2)
                self.debuger_ImgTabWid.pushInfo(f'{scaleNumHeight}')
            if imgSourceWidth > areaSourceWidth:
                scaleNumWidth = round((areaSourceWidth / imgSourceWidth), 2)
                self.debuger_ImgTabWid.pushInfo(f'{scaleNumWidth}')

            if scaleNumWidth > scaleNumHeight:
                self.globalImageBox.ImageGraph.setScale(scaleNumHeight)
            else:
                self.globalImageBox.ImageGraph.setScale(scaleNumWidth)

            self.showGraphicsScene.addItem(self.globalImageBox.ImageGraph)
            # ----- 以上实现了图片载入到场景中并且按照一定比例缩放 ----- #

            self.splitTreeWidget.clear()

            root = QTreeWidgetItem(self.splitTreeWidget)
            root.setText(0, '类别')
            root.setIcon(0, QIcon('static/imgs/class.png'))
            # TODO:获取点位信息完善树状图
            # ----- 预设置了类树状图区域 ----- #

            scale = self.globalImageBox.ImageGraph.scale()
            self.imageStatusLabel.setText(f'(Name：{fileName}, Size：{fileSize})')
            self.imageInfoLabel.setText(f'(W:{imgSourceWidth},H:{imgSourceHeight} Scale:{scale * 100}%)')

    def __actionCreateProject(self):
        """
        描述：创建新工程
        功能：选择一个文件夹，该文件夹应该是原图片存储的地方，
            然后生成一个工程文件，方便下次载入后使用。
        参数：无
        返回值：无
        TODO: 在保存和不保存操作后，重置所有操作区域。
        """
        self.globalImageBox = None
        self.showGraphicsScene.clear()
        self.imageStatusLabel.setText('(Name：XXX, Size：XXX)')
        self.imageInfoLabel.setText('(W:XXX,H:XXX Scale:XX%)')
        self.mouseAndColorPointLabel.setText('[Clicked(XXX,XXX) RGB(XXX,XXX,XXX)]')

        self.debuger_ActCrePro = Debuger(100, True, 'CreateProject')
        if self.projectCreatedSignal:
            # 如果工程已经创建，则询问是否保存当前工程
            select = QMessageBox.information(self, '提示', '即将打开新的工程，请问是否保存当前工程？', QMessageBox.Yes | QMessageBox.No)
            self.debuger_ActCrePro.pushInfo(f'用户对新建工程是否保存当前工程的操作是[{select}]')
            if select == QMessageBox.Yes:
                with open(self.globalWorkPathFile, 'w', encoding='utf-8') as fp:
                    fp.write(json.dumps(self.projectFileContent))
                    fp.close()
        self.projectFileContent = []
        self.globalImageDics = {}
        self.globalWorkPath = ''
        self.globalWorkPathFile = ''
        workSpacePaths = QFileDialog.getSaveFileName(self, '选择工程文件夹', '/', 'spltd(*.spltd)')
        if workSpacePaths[0] != '':
            workSpacePathList = workSpacePaths[0].split('/')
            workSpacePath = ''
            for i in range(0, len(workSpacePathList) - 1):
                workSpacePath += workSpacePathList[i] + '/'
            if re.search(r'.spltd', workSpacePaths[0]) is None:
                self.globalWorkPathFile = workSpacePaths[0] + '.spltd'
            else:
                self.globalWorkPathFile = workSpacePaths[0]
            self.debuger_ActCrePro.pushInfo(f'用户选择了工程文件夹路径为[{self.globalWorkPathFile}]')
            self.globalWorkPath = workSpacePath
            self.projectShowLineEdit.setText(self.globalWorkPathFile)
            self.actionSaveProject.setEnabled(True)
            self.actionAddPage.setEnabled(True)
            self.actionSaveNowProject.setEnabled(True)
            # 显示用户工程路径和激活保存按钮和图像添加按钮
            nums = 0
            fileNameList = []
            for fileName in os.listdir(workSpacePath):
                res = os.path.splitext(fileName)[1]
                if res in self.supportImageTuple:
                    nums += 1
                    fileNameList.append(fileName)
            if len(fileNameList) != 0:
                self.projectFileContent.append({'PreLoadImageContent': [], 'WorkPath': self.globalWorkPath})
                self.imageTableWidget.setRowCount(nums)
                pnums = 0
                for fileName in fileNameList:
                    fileAbsPath = os.path.join(workSpacePath, fileName)
                    fileSizeMB = str(round(os.path.getsize(fileAbsPath) / 1024, 2)) + 'KB'
                    fileLastTime = time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(os.path.getmtime(fileAbsPath)))

                    fileNameItem = QTableWidgetItem(QIcon('static/imgs/tupian.png'), fileName)
                    fileSizeMBItem = QTableWidgetItem(fileSizeMB)
                    fileLastTimeItem = QTableWidgetItem(fileLastTime)

                    # fileNameItem.data()

                    self.imageTableWidget.setItem(pnums, 0, fileNameItem)
                    self.imageTableWidget.setItem(pnums, 1, fileSizeMBItem)
                    self.imageTableWidget.setItem(pnums, 2, fileLastTimeItem)
                    self.projectFileContent[0]['PreLoadImageContent'].append({'index': pnums, 'fileName': fileName,
                                                                              'fileSize': fileSizeMB,
                                                                              'fileLastTime': fileLastTime,
                                                                              'fileAbsPath': fileAbsPath})
                    self.debuger_ActCrePro.pushInfo(f'分割出来的结果为：{(fileName, fileAbsPath, fileSizeMB, fileLastTime)}')
                    pnums += 1
                self.projectCreatedSignal = True
                # 遍历文件，存储找到的图片信息到全局工程内容中，在列表里显示图片预选择基本信息。

    def __actionOpenProject(self):
        """
        描述：打开工程
        功能：打开工程文件，载入工程文件里的所有设置参数，直接加载上一次没有完成的动作
        参数：无
        返回值：无
        """
        # TODO:重置操作 | 载入分割节点信息到类别树状图标中去
        self.globalImageBox = None
        self.showGraphicsScene.clear()
        self.imageStatusLabel.setText('(Name：XXX, Size：XXX)')
        self.imageInfoLabel.setText('(W:XXX,H:XXX Scale:XX%)')
        self.mouseAndColorPointLabel.setText('[Clicked(XXX,XXX) RGB(XXX,XXX,XXX)]')

        # ----- 重置操作 ----- #
        self.debuger_ActOpePro = Debuger(100, True, 'OpenProject')

        if self.projectCreatedSignal:
            select = QMessageBox.information(self, '保存当前工程', '已经打开了一个工程，是否保存当前工程?', QMessageBox.Yes | QMessageBox.No)
            self.debuger_ActOpePro.pushInfo(f'{select}')
            if select == QMessageBox.Yes:
                with open(self.globalWorkPathFile, 'w', encoding='utf-8') as fp:
                    fp.write(json.dumps(self.projectFileContent))
                    fp.close()
        self.globalWorkPath = ''
        self.globalWorkPathFile = ''
        self.projectFileContent = []
        self.globalImageDics = {}
        filePath, type = QFileDialog.getOpenFileName(self, '选择工程文件(*.spltd)', '/', 'spltd(*.spltd)')
        if filePath != '':
            self.globalWorkPathFile = filePath
            self.debuger_ActOpePro.pushInfo(f'用户打开的工程文件路径为[{filePath}]')

            if re.search(r'.spltd', type) is not None:
                with open(filePath, 'r', encoding='utf-8') as fp:
                    resDict = json.loads(fp.read())
                    fp.close()
                self.projectFileContent = resDict
                fonded = False
                try:
                    self.globalImageDics = resDict[1]['SplitNodeImageContent']
                    fonded = True
                except IndexError as e:
                    self.debuger_ActOpePro.pushInfo(f'{repr(e)}')
                    self.debuger_ActOpePro.pushInfo('未能找到分割节点信息')
                    fonded = False
                    # self.debuger_ActOpePro.pushInfo(f'{resDict}')
                    if len(resDict[0]['PreLoadImageContent']) == 0:
                        self.debuger_ActOpePro.pushInfo('没有任何可读取内容')
                    else:
                        self.projectCreatedSignal = True
                        self.globalWorkPath = resDict[0]['WorkPath']
                        self.projectShowLineEdit.setText(self.globalWorkPathFile)
                        self.actionSaveProject.setEnabled(True)
                        self.actionAddPage.setEnabled(True)
                        self.actionSaveNowProject.setEnabled(True)
                        self.imageTableWidget.setRowCount(len(resDict[0]['PreLoadImageContent']))
                        pnums = 0
                        for file in resDict[0]['PreLoadImageContent']:
                            fileNameItem = QTableWidgetItem(QIcon('static/imgs/tupian.png'), file['fileName'])
                            fileSizeMBItem = QTableWidgetItem(file['fileSize'])
                            fileLastTimeItem = QTableWidgetItem(file['fileLastTime'])

                            # fileNameItem.data()

                            self.imageTableWidget.setItem(pnums, 0, fileNameItem)
                            self.imageTableWidget.setItem(pnums, 1, fileSizeMBItem)
                            self.imageTableWidget.setItem(pnums, 2, fileLastTimeItem)
                            pnums += 1
                        if fonded:
                            # TODO: 载入分割节点信息到类别树状图标中去
                            pass

    def __actionSaveProject(self):
        """
        描述：保存工程
        功能：对工程进行保存操作
        参数：无
        返回值：无
        """
        self.debuger_ActSavPro = Debuger(100, True, 'SaveProject')
        with open(self.globalWorkPathFile, 'w', encoding='utf-8') as fp:
            fp.write(json.dumps(self.projectFileContent))
            fp.close()
        self.debuger_ActSavPro.pushInfo('用户已点击保存')

    def __actionSaveNowProject(self):
        """
        描述：另存工程文件
        功能：另存工程文件在其他地方
        参数：无
        返回值：无
        """
        self.debuger_ActSavNowPro = Debuger(100, True, 'SaveNowProject')
        filePath, type = QFileDialog.getSaveFileName(self, '另存文件为...', self.globalWorkPath, '.spltd(*.spltd)')
        self.debuger_ActSavNowPro.pushInfo(f'{(filePath, type)}')
        if re.search(r'.spltd', filePath) is not None:
            with open(filePath, 'w', encoding='utf-8') as fp:
                fp.write(json.dumps(self.projectFileContent))
                fp.close()
        else:
            filePath += '.spltd'
            with open(filePath, 'w', encoding='utf-8') as fp:
                fp.write(json.dumps(self.projectFileContent))
                fp.close()

    def __actionExit(self):
        """
        描述：退出
        功能：退出
        参数：无
        返回值：无
        """
        self.debuger_ActExit = Debuger(50, True, 'Exit')
        if self.projectCreatedSignal:
            select = QMessageBox.information(self, '保存当前工程', '退出程序前是否保存工程?', QMessageBox.Yes | QMessageBox.No)
            self.debuger_ActExit.pushInfo(f'{select}')
            if select == QMessageBox.Yes:
                self.__actionSaveProject()
        app = QApplication.instance()
        app.quit()

    # ----- 菜单栏的工程设置函数 ----- #

    def __actionNextPage(self):
        """
        描述：下一张图片
        功能：切换到下一个索引图片
        参数：无
        返回值：无
        """
        self.debuger_ActNexPag = Debuger(50, True, 'NextPage')
        curRow = self.imageTableWidget.currentRow()
        self.imageTableWidget.selectRow(curRow + 1)
        self.debuger_ActNexPag.pushInfo(curRow + 1)

    def __actionLastPage(self):
        """
        描述：上一张图片
        功能：切换到上一个索引图片
        参数：无
        返回值：无
        """
        self.debuger_ActNexPag = Debuger(50, True, 'NextPage')
        curRow = self.imageTableWidget.currentRow()
        self.imageTableWidget.selectRow(curRow - 1)
        self.debuger_ActNexPag.pushInfo(curRow - 1)

    def __actionDeletePage(self):
        """
        描述：删除图片
        功能：从场景中移除该图片，并且清空图片盒子的所有信息，移除图片列表的信息
        参数：无
        返回值：无
        """
        self.debuger_ActDelPag = Debuger(50, True, 'DeletePage')
        self.showGraphicsScene.clear()
        self.globalImageBox = None
        if len(self.projectFileContent) == 2:
            self.projectFileContent.pop()
            self.projectFileContent[0]["PreLoadImageContent"].pop(self.imageTableWidget.currentRow())
        else:
            res = self.projectFileContent[0]["PreLoadImageContent"].pop(self.imageTableWidget.currentRow())
            self.debuger_ActDelPag.pushInfo(f'{(self.imageTableWidget.currentRow(), res)}')
        self.splitTreeWidget.clear()
        self.imageTableWidget.removeRow(self.imageTableWidget.currentRow())
        if len(self.projectFileContent[0]["PreLoadImageContent"]) == 0:
            self.actionDeletePage.setEnabled(False)

    def __actionAddPage(self):
        self.actionDeletePage.setEnabled(True)
        self.debuger_ActAddPag = Debuger(50, True, 'AddPage')
        fileList, type = QFileDialog.getOpenFileNames(self, '选择多张图片文件', self.globalWorkPath,
                                                  'JPG(*.jpg);;JPEG(*.jpeg);;BMP(*.bmp);;PNG(*.png)')
        self.debuger_ActAddPag.pushInfo(f'{(fileList, type)}')
        for file in fileList:
            if file != '':
                if re.search(r'(.jpg)|(.jpeg)|(.bmp)|(.png)', file) is not None:
                    pass
                else:
                    file += re.search(r'\.[a-zA-Z]+', type).group()[0]
                    self.debuger_ActAddPag.pushInfo(f'{file}')
                index = len(self.projectFileContent[0]['PreLoadImageContent'])
                fileNameList = file.split('/')
                fileName = fileNameList[len(fileNameList) - 1]
                fileSize = str(round(os.path.getsize(file) / 1024, 2)) + 'KB'
                fileLastTime = time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(os.path.getmtime(file)))
                fileAbsPath = file
                self.projectFileContent[0]['PreLoadImageContent'].append({'index': index, 'fileName': fileName,
                                                                          'fileSize': fileSize,
                                                                          'fileLastTime': fileLastTime,
                                                                          'fileAbsPath': fileAbsPath})

                fileNameItem = QTableWidgetItem(QIcon('static/imgs/tupian.png'), fileName)
                fileSizeMBItem = QTableWidgetItem(fileSize)
                fileLastTimeItem = QTableWidgetItem(fileLastTime)
                self.imageTableWidget.insertRow(index)
                self.imageTableWidget.setItem(index, 0, fileNameItem)
                self.imageTableWidget.setItem(index, 1, fileSizeMBItem)
                self.imageTableWidget.setItem(index, 2, fileLastTimeItem)

    # ----- 菜单栏的图片设置函数 ----- #

    def __actionNewLabel(self):
        pass

    def __actionEditLabel(self):
        pass

    def __actionDeleteLabel(self):
        pass

    # ----- 菜单栏的标签设置函数 ----- #

    def __actionUndoPixel(self):
        pass

    def __actionRedoPixel(self):
        pass

    def __actionRefreshPixel(self):
        pass

    # ----- 菜单栏的点位设置函数 ----- #

    def __actionMutiRename(self):
        pass

    def __actionChangeFileSpace(self):
        pass

    def __actionOutDelete(self):
        pass

    def __actionOutDeleteAll(self):
        pass

    def __actionOut(self):
        pass
    # ----- 菜单栏的输出设置函数 ----- #


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = OpratorWindow()
    main_window.windowShow()
    sys.exit(app.exec_())
